(function (define) {
    define(['jquery', 'layer'], function ($, layer) {
        "use strict"

        if ($ === undefined) {
            throw new Error("插件依赖jQuery，请先引入jQuery")
        }

        /**
         * 发送请求类
         */
        class Request {
            _options = {
                baseUrl: "",
                url: "",
                type: "GET",
                data: null,
                extraData: null,
                dataType: "json",
                timeout: 0
            }

            constructor(baseUrl, timeout = 10000) {
                this._options.baseUrl = baseUrl
                this._options.timeout = timeout
            }

            jsonp(jsonp, jsonpCallback) {
                this._options.dataType = "jsonp" //发送jsonp请求
                jsonp && (this._options.jsonp = jsonp) //客户端用callback作为参数名将函数传递给服务器端
                jsonpCallback && (this._options.jsonpCallback = jsonpCallback) //不想执行success回调函数，可指定函数名

                return this
            }

            get(url, data = {}) {
                this._options.type = "GET"
                this._options.url = url
                this._options.data = data
                return this.send(this._options)
            }

            post(url, data = {}) {
                this._options.type = "POST"
                this._options.url = url
                this._options.data = data
                return this.send(this._options)
            }

            extraData(data) {
                this._options.extraData = data
                return this
            }

            send(options) {
                if (options.extraData) {
                    options.data = {...options.data, ...options.extraData}
                }
                if (options.baseUrl) {
                    options.url = options.baseUrl + options.url
                }

                return new Promise(function (resolve, reject) {
                    $.ajax({
                        url: options.url,
                        type: options.type,
                        data: options.data,
                        dataType: options.dataType,
                        timeout: options.timeout,
                        success: function (res) {
                            resolve(res)
                        },
                        error: function (err) {
                            reject(err)
                        }
                    })
                })
            }
        }

        /**
         * 工具类
         */
        class Utils {
            /**
             * 防抖
             * @param fn
             * @param delay
             * @param immediate
             * @returns {_debounce}
             */
            static debounce(fn, delay, immediate = false) {
                // 1.定义一个定时器, 保存上一次的定时器
                let timer = null
                let isInvoke = false

                // 2.真正执行的函数
                const _debounce = function (...args) {
                    // 取消上一次的定时器
                    if (timer) clearTimeout(timer)

                    // 判断是否需要立即执行
                    if (immediate && !isInvoke) {
                        fn.apply(this, args)
                        isInvoke = true
                    } else {
                        // 延迟执行
                        timer = setTimeout(() => {
                            // 外部传入的真正要执行的函数
                            fn.apply(this, args)
                            isInvoke = false
                            timer = null
                        }, delay)
                    }
                }

                // 封装取消功能
                _debounce.cancel = function () {
                    if (timer) clearTimeout(timer)
                    timer = null
                    isInvoke = false
                }

                return _debounce
            }
        }

        class AddrHelper {
            _options = {
                key: "", //必传，腾讯地图api key 申请方法见：https://lbs.qq.com/webApi/javascriptGL/glGuide/glBasic
                el: "", //可选项，渲染容器，为空则以弹窗形式打开
                lat: 0, //可选项，初始化纬度
                lng: 0, //可选项，初始化经度
                zoom: 13, //可选项，地图缩放级别
                title: "腾讯地图小助手", //可选项，弹窗标题
                width: "80vw", //可选项，弹窗的宽度
                height: "80vh", //可选项，弹窗的高度
                toolbar: true, //可选项，显示工具栏
                success: null, //可选项，地址选择成功后回调
                cssDebug: false //可选项，主要为开发时调试样式
            }
            map = null
            markerLayer = null
            geometryEditor = null
            overlayZIndex = 9999
            controlTypeMap = null
            controlPositionMap = null
            editorModeMap = null
            request = null
            layerIndex = 0
            locationInfo = null
            suggestionOptions = null
            selectAddressInfo = null
            selectGeometry = null

            render(options) {
                this._options = {...this._options, ...options}
                if (!this._options.key) {
                    throw new Error("参数key必传")
                }
                if (layer === undefined && !this._options.el) {
                    throw new Error("参数el必传")
                }
                this.request = new Request("https://apis.map.qq.com").jsonp().extraData({key: this._options.key, output: "jsonp"})
                this.dynamicLoadHtml()
                !this._options.cssDebug && this.dynamicLoadCss()
                this.eventListen()
                //注意：不支持file://方式使用Javascript API GL 详见 https://lbs.qq.com/webApi/javascriptGL/glGuide/glBasic
                this.dynamicLoadJs(`https://map.qq.com/api/gljs?v=1.exp&key=${this._options.key}&libraries=tools,geometry`, () => {
                    if (this._options.lat && this._options.lng) {
                        this.initMap(Number(this._options.lat), Number(this._options.lng))
                    } else {
                        this.initMap()
                    }
                })
            }

            close(v) {
                this.layerIndex && layer.close(this.layerIndex)
            }

            ok() {
                if (this._options.success && typeof this._options.success === "function") {
                    this._options.success.call(this, {
                        addressInfo: this.selectAddressInfo,
                        geometryPaths: this.selectGeometry?.paths ?? null
                    })
                }
            }

            dynamicLoadHtml() {
                const _this = this;
                const content = `
                <div class="addrhelper-getpoint">
                    <!-- 坐标拾取 -->
                    <div class="addrhelper-getpoint-map">
                        <div id="addrhelper-map-container"></div>
                        <div class="addrhelper-getpoint-search">
                            <input type="text" class="addrhelper-search-input" placeholder="输入地址" value="">
                        </div>
                        <div class="addrhelper-getpoint-tips"></div>
                        <div class="addrhelper-search-suggestion">
                            <div class="addrhelper-search-show-btn">
                                <img src="" alt="">
                                <span>展开搜索列表</span>
                            </div>
                            <div class="addrhelper-search-list"></div>
                        </div>
                        <div class="addrhelper-satellite">
                            <span class="icon"></span>
                            <span>卫星</span>
                        </div>
                        ${this._options.toolbar ? `<div class="addrhelper-toolbar">
                            <div data-action="marker" class="tool tool-marker tool-active" title="点标记"></div>
                            <div data-action="polygon" class="tool tool-polygon" title="多边形"></div>
                            <div data-action="circle" class="tool tool-circle" title="圆形"></div>
                            <div data-action="rectangle" class="tool tool-rectangle" title="矩形"></div>
                            <div data-action="ellipse" class="tool tool-ellipse" title="椭圆"></div>
                            <div data-action="delete" class="tool tool-delete" title="删除"></div>
                            <div data-action="split" class="tool tool-split" title="拆分"></div>
                            <div data-action="union" class="tool tool-union" title="合并"></div>
                            <div data-action="manual" class="tool tool-manual" title="手册"></div>
                        </div>` : ''}
                    </div>
                    <!-- 坐标信息 -->
                    <div class="addrhelper-getpoint-info">
                        <div class="title">点图获取坐标</div>
                        <div class="item">
                            <div class="label">经度</div>
                            <div class="input lng"></div>
                        </div>
                        <div class="item">
                            <div class="label">纬度</div>
                            <div class="input lat"></div>
                        </div>
                        <div class="item">
                            <div class="label">地址</div>
                            <div class="input address"></div>
                        </div>
                        <div class="item">
                            <div class="label">POI ID</div>
                            <div class="input poi"></div>
                        </div>
                        ${this._options.el ? '<button class="addrhelper-ok-btn">确定</button>' : ''}
                    </div>
                </div>`
                if (this._options.el) {
                    $(this._options.el).width(this._options.width).height(this._options.height).html(content)
                } else {
                    this.layerIndex = layer.open({
                        type: 1,
                        title: this._options.title,
                        content: content,
                        area: [this._options.width, this._options.height],
                        btn: ['确定', '取消'],
                        maxmin: true,
                        yes: function (index, layero) {
                            _this.ok()
                        },
                        cancel: function () {
                            //右上角关闭回调
                            //return false 开启该代码可禁止点击该按钮关闭
                        }
                    })
                }
            }

            dynamicLoadCss() {
                if (!$('style#addrHelperCSS').length) {
                    $("head").append(`<style id="addrHelperCSS">
                    .addrhelper-text-ellipsis {
                      white-space: nowrap;
                      overflow: hidden;
                      text-overflow: ellipsis;
                    }
                    .addrhelper-tool-manual {
                      padding: 10px;
                    }
                    .addrhelper-tool-manual .title {
                      margin-bottom: 4px;
                      font-size: 15px;
                      color: #1b202c;
                      line-height: 22px;
                      font-weight: 600;
                    }
                    .addrhelper-tool-manual .tip {
                      margin-bottom: 5px;
                      color: #999999;
                      font-size: 14px;
                    }
                    .addrhelper-tool-manual .tip .strong {
                      font-weight: 600;
                    }
                    .addrhelper-getpoint {
                      height: 100%;
                      width: 100%;
                      display: flex;
                    }
                    .addrhelper-getpoint .addrhelper-cursor-point {
                      cursor: url(""), auto;
                    }
                    .addrhelper-getpoint .addrhelper-getpoint-map {
                      width: 75%;
                      position: relative;
                    }
                    .addrhelper-getpoint .addrhelper-getpoint-map #addrhelper-map-container {
                      height: 100%;
                      width: 100%;
                    }
                    .addrhelper-getpoint .addrhelper-getpoint-map .addrhelper-getpoint-search {
                      display: flex;
                      position: absolute;
                      top: 20px;
                      left: 20px;
                      width: 35%;
                      height: 36px;
                      border: 1px solid #F3F3F3;
                      box-sizing: border-box;
                      border-radius: 5px;
                      line-height: 36px;
                      background-color: #FFFFFF;
                      z-index: 9999;
                    }
                    .addrhelper-getpoint .addrhelper-getpoint-map .addrhelper-getpoint-search:hover {
                      border: 1px solid #CCCCCC;
                    }
                    .addrhelper-getpoint .addrhelper-getpoint-map .addrhelper-getpoint-search input {
                      width: 100%;
                      flex: 1;
                      padding-left: 8px;
                      border: none;
                      outline: none;
                      border-radius: 5px;
                    }
                    .addrhelper-getpoint .addrhelper-getpoint-map .addrhelper-search-suggestion {
                      width: 35%;
                      max-height: 50%;
                      overflow-y: auto;
                      box-sizing: border-box;
                      position: absolute;
                      top: 56px;
                      left: 20px;
                      background-color: #FFFFFF;
                      z-index: 9999;
                    }
                    .addrhelper-getpoint .addrhelper-getpoint-map .addrhelper-search-suggestion::-webkit-scrollbar {
                      width: 6px;
                      height: 6px;
                      background-color: #F5F5F5;
                    }
                    .addrhelper-getpoint .addrhelper-getpoint-map .addrhelper-search-suggestion::-webkit-scrollbar-thumb {
                      background-color: #DDDEE0;
                      background-clip: padding-box;
                      border-radius: 3px;
                    }
                    .addrhelper-getpoint .addrhelper-getpoint-map .addrhelper-search-suggestion .addrhelper-search-show-btn {
                      height: 0;
                      overflow: hidden;
                      display: flex;
                      justify-content: center;
                      align-items: center;
                      cursor: pointer;
                    }
                    .addrhelper-getpoint .addrhelper-getpoint-map .addrhelper-search-suggestion .addrhelper-search-show-btn img {
                      height: 23px;
                      width: 23px;
                      transform: rotate(90deg);
                    }
                    .addrhelper-getpoint .addrhelper-getpoint-map .addrhelper-search-suggestion .addrhelper-search-show-btn span {
                      opacity: 0.8;
                      font-size: 14px;
                      color: #1B202C;
                      font-weight: 400;
                    }
                    .addrhelper-getpoint .addrhelper-getpoint-map .addrhelper-search-suggestion .addrhelper-search-list .addrhelper-search-address {
                      box-sizing: border-box;
                      padding: 0 15px;
                    }
                    .addrhelper-getpoint .addrhelper-getpoint-map .addrhelper-search-suggestion .addrhelper-search-list .addrhelper-search-address:hover {
                      background-color: #F5F9FF;
                      cursor: pointer;
                    }
                    .addrhelper-getpoint .addrhelper-getpoint-map .addrhelper-search-suggestion .addrhelper-search-list .addrhelper-search-address .border {
                      display: flex;
                      align-items: center;
                      box-sizing: border-box;
                      padding: 10px 0;
                      border-bottom: 1px solid rgba(27, 32, 44, 0.05);
                    }
                    .addrhelper-getpoint .addrhelper-getpoint-map .addrhelper-search-suggestion .addrhelper-search-list .addrhelper-search-address .border .index {
                      height: 24px;
                      width: 24px;
                      background: rgba(0, 98, 255, 0.08);
                      margin-right: 8px;
                      font-size: 12px;
                      color: #0062FF;
                      text-align: center;
                      line-height: 24px;
                      font-weight: 400;
                      border-radius: 50%;
                    }
                    .addrhelper-getpoint .addrhelper-getpoint-map .addrhelper-search-suggestion .addrhelper-search-list .addrhelper-search-address .border .info {
                      width: calc(100% - 32px);
                    }
                    .addrhelper-getpoint .addrhelper-getpoint-map .addrhelper-search-suggestion .addrhelper-search-list .addrhelper-search-address .border .info .title {
                      font-size: 14px;
                      color: #1b202c;
                      line-height: 22px;
                      font-weight: 600;
                      margin-bottom: 4px;
                      white-space: nowrap;
                      overflow: hidden;
                      text-overflow: ellipsis;
                    }
                    .addrhelper-getpoint .addrhelper-getpoint-map .addrhelper-search-suggestion .addrhelper-search-list .addrhelper-search-address .border .info .address {
                      font-size: 12px;
                      color: #535B6E;
                      line-height: 18px;
                      font-weight: 400;
                      white-space: nowrap;
                      overflow: hidden;
                      text-overflow: ellipsis;
                    }
                    .addrhelper-getpoint .addrhelper-getpoint-map .addrhelper-search-suggestion .addrhelper-search-list .addrhelper-search-address-active {
                      background-color: #EBF3FF;
                    }
                    .addrhelper-getpoint .addrhelper-getpoint-map .addrhelper-search-suggestion .addrhelper-search-list .addrhelper-search-address-active .border .index {
                      background-color: #0062FF;
                      color: #FFFFFF;
                    }
                    .addrhelper-getpoint .addrhelper-getpoint-map .addrhelper-search-suggestion .addrhelper-search-list .addrhelper-search-address-active .border .info .title,
                    .addrhelper-getpoint .addrhelper-getpoint-map .addrhelper-search-suggestion .addrhelper-search-list .addrhelper-search-address-active .border .info .address {
                      color: #0062FF;
                    }
                    .addrhelper-getpoint .addrhelper-getpoint-map .addrhelper-satellite {
                      position: absolute;
                      top: 20px;
                      right: 20px;
                      z-index: 9999;
                      display: flex;
                      justify-content: center;
                      align-items: center;
                      width: 85px;
                      height: 36px;
                      font-size: 14px;
                      color: #1F2226;
                      background: #FFFFFF;
                      box-sizing: border-box;
                      border: 1px solid rgba(31, 34, 38, 0.1);
                      box-shadow: 0 4px 10px 0 #1F222612;
                      border-radius: 4px;
                      cursor: pointer;
                    }
                    .addrhelper-getpoint .addrhelper-getpoint-map .addrhelper-satellite:hover {
                      border-color: #A5A7A8;
                    }
                    .addrhelper-getpoint .addrhelper-getpoint-map .addrhelper-satellite .icon {
                      background-image: url();
                      background-size: 24px 24px;
                      background-repeat: no-repeat;
                      display: inline-block;
                      width: 24px;
                      height: 24px;
                    }
                    .addrhelper-getpoint .addrhelper-getpoint-map .addrhelper-satellite .icon-active {
                      background-image: url();
                    }
                    .addrhelper-getpoint .addrhelper-getpoint-map .addrhelper-satellite-active {
                      background-color: #EBF3FF;
                      color: #0062FF;
                    }
                    .addrhelper-getpoint .addrhelper-getpoint-map .addrhelper-toolbar {
                      position: absolute;
                      right: 20px;
                      bottom: 20px;
                      z-index: 9999;
                    }
                    .addrhelper-getpoint .addrhelper-getpoint-map .addrhelper-toolbar .tool {
                      width: 30px;
                      height: 30px;
                      float: left;
                      margin: 1px;
                      padding: 4px;
                      border-radius: 3px;
                      background-size: 30px 30px;
                      background-position: 4px 4px;
                      background-repeat: no-repeat;
                      box-shadow: 0 1px 2px 0 #e4e7ef;
                      background-color: #ffffff;
                      border: 1px solid #ffffff;
                    }
                    .addrhelper-getpoint .addrhelper-getpoint-map .addrhelper-toolbar .tool:hover {
                      border-color: #789cff;
                    }
                    .addrhelper-getpoint .addrhelper-getpoint-map .addrhelper-toolbar .tool-active {
                      border-color: #d5dff2;
                      background-color: #d5dff2;
                    }
                    .addrhelper-getpoint .addrhelper-getpoint-map .addrhelper-toolbar .tool-marker {
                      background-image: url("");
                    }
                    .addrhelper-getpoint .addrhelper-getpoint-map .addrhelper-toolbar .tool-polygon {
                      background-image: url("");
                    }
                    .addrhelper-getpoint .addrhelper-getpoint-map .addrhelper-toolbar .tool-circle {
                      background-image: url("");
                    }
                    .addrhelper-getpoint .addrhelper-getpoint-map .addrhelper-toolbar .tool-rectangle {
                      background-image: url("");
                    }
                    .addrhelper-getpoint .addrhelper-getpoint-map .addrhelper-toolbar .tool-ellipse {
                      background-image: url("");
                    }
                    .addrhelper-getpoint .addrhelper-getpoint-map .addrhelper-toolbar .tool-delete {
                      background-image: url("");
                    }
                    .addrhelper-getpoint .addrhelper-getpoint-map .addrhelper-toolbar .tool-split {
                      background-image: url("");
                    }
                    .addrhelper-getpoint .addrhelper-getpoint-map .addrhelper-toolbar .tool-union {
                      background-image: url("");
                    }
                    .addrhelper-getpoint .addrhelper-getpoint-map .addrhelper-toolbar .tool-manual {
                      background-image: url("");
                    }
                    .addrhelper-getpoint .addrhelper-getpoint-map .addrhelper-getpoint-tips {
                      position: absolute;
                      z-index: 9999;
                      background-color: #484847;
                      color: #FFFFFF;
                      padding: 5px;
                      border-radius: 3px;
                      font-size: 12px;
                    }
                    .addrhelper-getpoint .addrhelper-getpoint-info {
                      width: 25%;
                      background-color: #FFFFFF;
                      padding: 25px 20px;
                      box-sizing: border-box;
                      word-break: break-all;
                      position: relative;
                    }
                    .addrhelper-getpoint .addrhelper-getpoint-info .title {
                      font-size: 16px;
                      color: #1b202c;
                      letter-spacing: 0;
                      line-height: 24px;
                      font-weight: 600;
                      min-height: 24px;
                    }
                    .addrhelper-getpoint .addrhelper-getpoint-info .item {
                      margin-top: 20px;
                    }
                    .addrhelper-getpoint .addrhelper-getpoint-info .item .label {
                      margin-bottom: 4px;
                      font-size: 14px;
                      color: #1b202c;
                      letter-spacing: 0;
                      line-height: 22px;
                      font-weight: 600;
                    }
                    .addrhelper-getpoint .addrhelper-getpoint-info .item .input {
                      padding: 0 40px 0 8px;
                      min-height: 34px;
                      line-height: 34px;
                      background: rgba(27, 32, 44, 0.03);
                      border: 1px solid #ced2d9;
                      border-radius: 4px;
                      font-size: 14px;
                      color: #1b202c;
                      font-weight: 400;
                    }
                    .addrhelper-getpoint .addrhelper-getpoint-info .addrhelper-ok-btn {
                      outline: none;
                      border: none;
                      position: absolute;
                      right: 20px;
                      bottom: 0;
                      height: 28px;
                      line-height: 28px;
                      padding: 0 15px;
                      border-radius: 2px;
                      background-color: #1E9FFF;
                      color: #FFF;
                      font-weight: 400;
                      cursor: pointer;
                    }
                    .addrhelper-getpoint .addrhelper-getpoint-info .addrhelper-ok-btn:hover {
                      opacity: 0.8;
                    }
                </style>`)
                }
            }

            /**
             * 动态加载 js
             * @param url
             * @param callback
             */
            dynamicLoadJs(url, callback) {
                var script = document.createElement('script');
                script.type = 'text/javascript';
                script.src = url;
                if (typeof (callback) == 'function') {
                    script.onload = script.onreadystatechange = function () {
                        if (!this.readyState || this.readyState === "loaded" || this.readyState === "complete") {
                            callback();
                            script.onload = script.onreadystatechange = null;
                        }
                    };
                }
                document.body.appendChild(script);
            }

            /**
             * 初始化地图
             * @param lat
             * @param lng
             * @returns {Promise<void>}
             */
            async initMap(lat, lng) {
                if (!lat || !lng) {
                    let ipLocationReturn = await this.ipLocation()
                    if (ipLocationReturn.status === 0) {
                        lat = ipLocationReturn.result.location.lat
                        lng = ipLocationReturn.result.location.lng
                        this.locationInfo = ipLocationReturn.result
                    }
                }

                //https://lbs.qq.com/webApi/javascriptGL/glDoc/docIndexMap
                this.map = new TMap.Map("addrhelper-map-container", {
                    center: new TMap.LatLng(lat, lng),
                    zoom: this._options.zoom
                })

                //https://lbs.qq.com/webApi/javascriptGL/glDoc/glDocMarker
                this.markerLayer = new TMap.MultiMarker({
                    map: this.map
                })

                //附加库：地图工具 使用此库 地图会变成 2D 的
                //https://lbs.qq.com/webApi/javascriptGL/glGuide/glEditor
                //https://lbs.qq.com/webApi/javascriptGL/glDoc/glDocEditor
                this.geometryEditor = new TMap.tools.GeometryEditor({
                    map: this.map,
                    overlayList: [
                        {
                            id: 'polygon',
                            // https://lbs.qq.com/webApi/javascriptGL/glDoc/glDocVector#7
                            overlay: new TMap.MultiPolygon({
                                map: this.map,
                                zIndex: this.overlayZIndex
                            }),
                        },
                        {
                            id: 'circle',
                            // https://lbs.qq.com/webApi/javascriptGL/glDoc/glDocVector#13
                            overlay: new TMap.MultiCircle({
                                map: this.map,
                            }),
                        },
                        {
                            id: 'rectangle',
                            // https://lbs.qq.com/webApi/javascriptGL/glDoc/glDocVector#MultiRectangle
                            overlay: new TMap.MultiRectangle({
                                map: this.map,
                            }),
                        },
                        {
                            id: 'ellipse',
                            // https://lbs.qq.com/webApi/javascriptGL/glDoc/glDocVector#MultiEllipse
                            overlay: new TMap.MultiEllipse({
                                map: this.map,
                            }),
                        },
                    ],
                    actionMode: TMap.tools.constants.EDITOR_ACTION.INTERACT,
                    activeOverlayId: "polygon",
                    snappable: true,
                    selectable: true
                })

                this.initMapConstant()
                this.initMapListen()

                this.setZoomControl("bottom-left", true)
                this.removeControl("rotation")

                if (this._options.created && typeof this._options.created === "function") {
                    this._options.created.call(this)
                }
            }

            initMapConstant() {
                this.controlTypeMap = {
                    scale: TMap.constants.DEFAULT_CONTROL_ID.SCALE,
                    zoom: TMap.constants.DEFAULT_CONTROL_ID.ZOOM,
                    rotation: TMap.constants.DEFAULT_CONTROL_ID.ROTATION,
                }

                this.controlPositionMap = {
                    "top-left": TMap.constants.CONTROL_POSITION.TOP_LEFT,
                    "top-center": TMap.constants.CONTROL_POSITION.TOP_CENTER,
                    "top-right": TMap.constants.CONTROL_POSITION.TOP_RIGHT,
                    "center-left": TMap.constants.CONTROL_POSITION.CENTER_LEFT,
                    "center": TMap.constants.CONTROL_POSITION.CENTER,
                    "center-right": TMap.constants.CONTROL_POSITION.CENTER_RIGHT,
                    "bottom-left": TMap.constants.CONTROL_POSITION.BOTTOM_LEFT,
                    "bottom-center": TMap.constants.CONTROL_POSITION.BOTTOM_CENTER,
                    "bottom-right": TMap.constants.CONTROL_POSITION.BOTTOM_RIGHT,
                }

                this.editorModeMap = {
                    draw: TMap.tools.constants.EDITOR_ACTION.DRAW,
                    interact: TMap.tools.constants.EDITOR_ACTION.INTERACT,
                }
            }

            initMapListen() {
                this.mapListen()
                this.geometryEditorListen()
            }

            mapListen() {
                const _this = this

                this.map.on("mousemove", function (event) {
                    let lat = event.latLng.getLat().toFixed(6);
                    let lng = event.latLng.getLng().toFixed(6);
                    $(".addrhelper-getpoint-tips").css({
                        top: event.point.y + 18,
                        left: event.point.x + 18
                    }).html(`${lat},${lng}`).show()

                    // 鼠标吸附效果
                    // $("#addrhelper-map-container").addClass("addrhelper-cursor-point")
                })

                this.map.on("mouseout", function (event) {
                    // $("#addrhelper-map-container").removeClass("addrhelper-cursor-point")
                    $(".addrhelper-getpoint-tips").hide()
                })

                this.map.on("click", async function (event) {
                    if (!_this.isDrawMode()) {
                        let lat = event.latLng.getLat().toFixed(6)
                        let lng = event.latLng.getLng().toFixed(6)
                        _this.setCurrentMarker(lat, lng)
                        const geocoderResponse = await _this.geocoder(lat, lng)
                        if (geocoderResponse.status === 0) {
                            const result = geocoderResponse.result
                            _this.reloadSelectAddress(lat, lng, result?.formatted_addresses?.recommend ?? "", result.address)
                            _this.setMapCenter(lat, lng)
                        }
                        // 隐藏搜索结果
                        if (_this.suggestionOptions !== null) {
                            $(".addrhelper-search-suggestion .addrhelper-search-list").hide()
                            $(".addrhelper-search-suggestion .addrhelper-search-show-btn").css("height", 38)
                            $(".addrhelper-search-suggestion .addrhelper-search-address").removeClass("addrhelper-search-address-active")
                        }
                    }
                })
            }

            geometryEditorListen() {
                const _this = this

                this.geometryEditor.on("draw_complete", function (geometry) {
                    ++_this.overlayZIndex
                    const activeOverlay = _this.getActiveOverlay()
                    activeOverlay.overlay.setZIndex(_this.overlayZIndex)
                    // console.log(activeOverlay.overlay.geometries)
                })

                this.geometryEditor.on("select", function (geometry) {
                    _this.selectGeometry = _this.geometryEditor.getSelectedList().pop()
                })

                this.geometryEditor.on("delete_complete", function (geometries) {
                    let findIndex = geometries.findIndex(function (item) {
                        return item.id === _this.selectGeometry.id
                    })
                    findIndex !== -1 && (_this.selectGeometry = null)
                })

                this.geometryEditor.on('split_fail', function (res) {
                    layer.msg(res.message)
                })

                this.geometryEditor.on('union_fail', function (res) {
                    layer.msg(res.message)
                })
            }

            eventListen() {
                this._options.el && this.okBtnListen()
                this.inputListen()
                this.addressSelectListen()
                this.showListListen()
                this.baseMapListen()
                this._options.toolbar && this.toolListen()
            }

            okBtnListen() {
                var _this = this;
                $('body').on('click', '.addrhelper-ok-btn', function () {
                    _this.ok();
                })
            }

            /**
             * 搜索监听
             */
            inputListen() {
                const _this = this
                $("body").on('input', ".addrhelper-search-input", Utils.debounce(async function (event) {
                    if (event.currentTarget.value) {
                        let region = _this.locationInfo ? _this.locationInfo.ad_info.city : ""
                        const suggestionReturn = await _this.suggestion(this.value, region)
                        let suggestion = ""
                        if (suggestionReturn.status === 0) {
                            _this.suggestionOptions = suggestionReturn.data
                            suggestionReturn.data.forEach(function (item, index, arr) {
                                suggestion += `
                                <div class="addrhelper-search-address" data-info='${JSON.stringify(item)}'>
                                    <div class="border">
                                        <div class="index">${index + 1}</div>
                                        <div class="info">
                                            <div class="title">${item.title} </div>
                                            <div class="address">${item.address}</div>
                                        </div>
                                    </div>
                                </div>`
                            })
                            $('.addrhelper-search-suggestion .addrhelper-search-show-btn').css("height", 0)
                            $('.addrhelper-search-suggestion .addrhelper-search-list').html(suggestion).show()
                        }
                    } else {
                        $('.addrhelper-search-suggestion .addrhelper-search-list').html("")
                        $('.addrhelper-search-suggestion .addrhelper-search-show-btn').css("height", 0)
                        _this.suggestionOptions = null
                        _this.locationInfo && _this.setMapCenter(_this.locationInfo.location.lat, _this.locationInfo.location.lng)
                    }
                }, 500))
            }

            /**
             * 地址选中监听
             */
            addressSelectListen() {
                const _this = this
                $("body").on("click", ".addrhelper-search-address", function (event) {
                    $(".addrhelper-search-address").removeClass("addrhelper-search-address-active")
                    $(this).addClass("addrhelper-search-address-active")
                    const adInfo = JSON.parse(event.currentTarget.dataset.info)
                    _this.reloadSelectAddress(adInfo.location.lat, adInfo.location.lng, adInfo.title, adInfo.address, adInfo.id)
                    _this.setMapCenter(adInfo.location.lat, adInfo.location.lng)
                    _this.setCurrentMarker(adInfo.location.lat, adInfo.location.lng)
                })
            }

            /**
             * 地址展开监听
             */
            showListListen() {
                $("body").on("click", ".addrhelper-search-suggestion .addrhelper-search-show-btn", function () {
                    $(this).css("height", 0)
                    $(".addrhelper-search-suggestion .addrhelper-search-list").show()
                })
            }

            /**
             * 底图监听
             */
            baseMapListen() {
                const _this = this
                $("body").on("click", ".addrhelper-satellite", function () {
                    let baseMapType = ""
                    if ($(this).hasClass("addrhelper-satellite-active")) {
                        $(this).removeClass("addrhelper-satellite-active")
                        $(this).find(".icon").removeClass("icon-active")
                        baseMapType = "vector"
                    } else {
                        $(this).addClass("addrhelper-satellite-active")
                        $(this).find(".icon").addClass("icon-active")
                        baseMapType = "satellite"
                    }
                    _this.map.setBaseMap({type: baseMapType})
                })
            }

            /**
             * 工具监听
             */
            toolListen() {
                let activeTool = $(".addrhelper-toolbar .tool-marker")
                const _this = this
                $("body").on("click", ".addrhelper-toolbar", function (event) {
                    if (event.target !== event.currentTarget) {
                        let action = event.target.dataset.action
                        const actionMap = {
                            delete: function () {
                                _this.geometryEditor.delete()
                            },
                            split: function () {
                                _this.geometryEditor.split()
                            },
                            union: function () {
                                _this.geometryEditor.union()
                            },
                            manual: function () {
                                layer.open({
                                    type: 1,
                                    title: false,
                                    content: `
                                    <div class="addrhelper-tool-manual">
                                        <div class="title">绘画工具操作说明：</div>
                                        <div class="tip"><span class="strong">单选</span>：鼠标左键点击图形</div>
                                        <div class="tip"><span class="strong">多选</span>：按下ctrl键后点击多个图形</div>
                                        <div class="tip"><span class="strong">删除</span>：选中图形后按下delete键或点击删除按钮可删除图形</div>
                                        <div class="tip"><span class="strong">编辑</span>：选中图形后出现编辑点，拖动编辑点可移动顶点位置，双击实心编辑点可删除顶点</div>
                                        <div class="tip"><span class="strong">拆分</span>：选中单个多边形后可绘制拆分线，拆分线绘制完成后自动进行拆分</div>
                                        <div class="tip"><span class="strong">合并</span>：选中多个相邻多边形后可进行合并，飞地形式的多边形不支持合并</div>
                                        <div class="tip"><span class="strong">中断</span>：按下esc键可中断当前操作，点选的图形将取消选中，编辑过程将中断</div>
                                    </div>`,
                                    closeBtn: 0,
                                    shadeClose: true
                                })
                            },
                            other: function () {
                                if (action !== "marker") {
                                    _this.setEditorMode("draw")
                                    _this.geometryEditor.setActiveOverlay(action)
                                } else {
                                    _this.setEditorMode("interact")
                                    _this.geometryEditor.stop()
                                }
                                activeTool && activeTool.removeClass("tool-active")
                                activeTool = $(event.target)
                                activeTool.addClass("tool-active")
                            }
                        }
                        const actionFunc = actionMap[action] === undefined ? actionMap.other : actionMap[action]
                        actionFunc()
                    }
                })
            }

            /**
             * 渲染选中地址
             * @param lat
             * @param lng
             * @param title
             * @param address
             * @param poi
             */
            reloadSelectAddress(lat, lng, title, address, poi = "") {
                $('.addrhelper-getpoint-info .title').html(title)
                $('.addrhelper-getpoint-info .lat').html(lat)
                $('.addrhelper-getpoint-info .lng').html(lng)
                $('.addrhelper-getpoint-info .address').html(address)
                $('.addrhelper-getpoint-info .poi').html(poi)
                this.selectAddressInfo = {lat, lng, title, address, poi}
            }

            /**
             * 设置地图中心
             * @param lat
             * @param lng
             * @link https://lbs.qq.com/webApi/javascriptGL/glGuide/glMap
             * @link https://lbs.qq.com/webApi/javascriptGL/glDoc/docIndexMap
             */
            setMapCenter(lat, lng) {
                this.map.easeTo(
                    {center: new TMap.LatLng(lat, lng)}
                )
                // this.map.setCenter(new TMap.LatLng(lat, lng))
            }

            /**
             * 点标记
             * @param lat
             * @param lng
             * @link https://lbs.qq.com/webApi/javascriptGL/glGuide/glMarker
             * @link https://lbs.qq.com/webApi/javascriptGL/glDoc/glDocMarker
             */
            setCurrentMarker(lat, lng) {
                this.markerLayer.setGeometries([])
                this.markerLayer.add([{
                    position: new TMap.LatLng(lat, lng)
                }])
            }

            /**
             * 控件
             * @link https://lbs.qq.com/webApi/javascriptGL/glGuide/glMarker
             * @link https://lbs.qq.com/webApi/javascriptGL/glDoc/glDocControl
             * @param type
             * @returns {*}
             */
            getControl(type) {
                if (this.controlTypeMap[type] === undefined) {
                    throw new Error("控件type非法")
                }
                return this.map.getControl(this.controlTypeMap[type])
            }

            /**
             * 移除控件
             * @param type
             * @returns {*}
             */
            removeControl(type) {
                if (this.controlTypeMap[type] === undefined) {
                    throw new Error("控件type非法")
                }
                return this.map.removeControl(this.controlTypeMap[type])
            }

            /**
             * 设置缩放控件
             * @param position
             * @param numVisible
             * @returns {*}
             */
            setZoomControl(position = "bottom-right", numVisible = false) {
                if (this.controlPositionMap[position] === undefined) {
                    position = "bottom-right"
                }
                return this.map.getControl("zoom").setPosition(this.controlPositionMap[position]).setNumVisible(numVisible)
            }

            /**
             * 是否是绘画模式
             * @returns {boolean}
             */
            isDrawMode() {
                return this.geometryEditor.getActionMode() === this.editorModeMap["draw"]
            }

            /**
             * 设置编辑器模式
             * @param mode
             */
            setEditorMode(mode) {
                if (this.editorModeMap[mode] === undefined) {
                    throw new Error("编辑器操作模式mode非法")
                }
                this.geometryEditor.setActionMode(this.editorModeMap[mode])
            }

            /**
             * 获取处于编辑状态的图层
             * @returns {*}
             */
            getActiveOverlay() {
                return this.geometryEditor.getActiveOverlay()
            }

            /**
             * 绘制多边形
             * @param styles 图层样式
             * @param geometries 多边形数据
             * @param editable 是否可编辑
             * @param zIndex 图层顺序
             * @returns {TMap.MultiPolygon|*}
             * @link https://lbs.qq.com/webApi/javascriptGL/glDoc/glDocVector#7
             * @link https://lbs.qq.com/webApi/javascriptGL/glDoc/glDocVector#10
             *
             */
            drawMultiPolygon(styles, geometries, editable = false, zIndex = 0) {
                if (!styles instanceof Object) {
                    throw new Error("styles参数必须是对象")
                }
                if (!Array.isArray(geometries)) {
                    throw new Error("geometries参数必须是数组")
                }

                const styleArr = []
                for (let key in styles) {
                    if (styles[key].borderColor !== undefined || styles[key].borderWidth !== undefined) {
                        styles[key].showBorder = true
                    }
                    if (styles[key].borderWidth === undefined) {
                        styles[key].borderWidth = 1
                    }
                    styles[key] = new TMap.PolygonStyle(styles[key])
                    styleArr.push(key)
                }

                for (let geometry of geometries) {
                    if (geometry.styleId === undefined) {
                        throw new Error("geometries缺少styleId属性")
                    }
                    if (!styleArr.includes(geometry.styleId)) {
                        throw new Error("geometries的styleId属性值非法")
                    }
                    if (geometry.paths === undefined) {
                        throw new Error("geometries缺少paths属性")
                    }
                    if (!Array.isArray(geometry.paths)) {
                        throw new Error("geometries的paths属性值必须是数组")
                    }

                    geometry.paths = geometry.paths.map(function (path) {
                        if (path.lat === undefined || path.lng === undefined) {
                            throw new Error("geometries的paths属性值格式非法")
                        }
                        return new TMap.LatLng(path.lat, path.lng);
                    })
                }

                if (editable) {
                    const activeOverlay = this.getActiveOverlay().overlay
                    activeOverlay.setStyles(styles)
                    activeOverlay.add(geometries)
                    return activeOverlay
                } else {
                    return new TMap.MultiPolygon({
                        map: this.map,
                        styles: styles,
                        geometries: geometries,
                        zIndex: zIndex
                    })
                }
            }

            /**
             * 绘制点标记
             * @param geometries
             * @returns {TMap.MultiMarker}
             * @link https://lbs.qq.com/webApi/javascriptGL/glDoc/glDocMarker#4
             */
            drawMultiMarker(geometries) {
                if (!Array.isArray(geometries)) {
                    throw new Error("geometries参数必须是数组")
                }

                const formatGeometries = []

                geometries.forEach(function (item) {
                    if (item.lat === undefined || item.lng === undefined) {
                        throw new Error("geometries参数格式非法")
                    }

                    formatGeometries.push({
                        position: new TMap.LatLng(item.lat, item.lng),
                        content: item?.content,
                    })
                })

                return new TMap.MultiMarker({
                    map: this.map,
                    geometries: formatGeometries
                })
            }

            /**
             * 点是否在多边形内
             * @param point
             * @param polygon
             * @returns {*}
             * @link https://lbs.qq.com/webApi/javascriptGL/glDoc/glDocGeometry
             */
            isPointInPolygon(point, polygon) {
                if (point.lat === undefined || point.lng === undefined) {
                    throw new Error("参数point格式非法")
                }
                const latLng = new TMap.LatLng(point.lat, point.lng)
                const latLngArr = polygon.map(function (path) {
                    if (path.lat === undefined || path.lng === undefined) {
                        throw new Error("参数polygon格式非法")
                    }
                    return new TMap.LatLng(path.lat, path.lng)
                })
                return TMap.geometry.isPointInPolygon(latLng, latLngArr)
            }

            /**
             * 判断多边形是否与多边形相交
             * @param polygon1
             * @param polygon2
             * @returns {*}
             * @link https://lbs.qq.com/webApi/javascriptGL/glDoc/glDocGeometry
             */
            isPolygonIntersect(polygon1, polygon2) {
                const latLngArr1 = polygon1.map(function (path) {
                    if (path.lat === undefined || path.lng === undefined) {
                        throw new Error("参数polygon1格式非法")
                    }
                    return new TMap.LatLng(path.lat, path.lng)
                })
                const latLngArr2 = polygon2.map(function (path) {
                    if (path.lat === undefined || path.lng === undefined) {
                        throw new Error("参数polygon2格式非法")
                    }
                    return new TMap.LatLng(path.lat, path.lng)
                })
                return TMap.geometry.isPolygonIntersect(latLngArr1, latLngArr2)
            }

            /**
             * 计算多边形的形心
             * @param polygon
             * @returns {*}
             * @link https://lbs.qq.com/webApi/javascriptGL/glDoc/glDocGeometry
             */
            computeCentroid(polygon) {
                const latLngArr = polygon.map(function (path) {
                    if (path.lat === undefined || path.lng === undefined) {
                        throw new Error("参数polygon格式非法")
                    }
                    return new TMap.LatLng(path.lat, path.lng)
                });

                return TMap.geometry.computeCentroid(latLngArr)
            }

            /**
             * IP定位
             * @returns {*}
             * @link https://lbs.qq.com/service/webService/webServiceGuide/webServiceIp
             */
            ipLocation() {
                return this.request.get("/ws/location/v1/ip")
            }

            /**
             * 关键词输入提示
             * @param keyword
             * @param region
             * @param regionFix
             * @returns {*}
             * @link https://lbs.qq.com/service/webService/webServiceGuide/webServiceSuggestion
             */
            suggestion(keyword, region = "", regionFix = 0) {
                return this.request.get('/ws/place/v1/suggestion', {
                    keyword,
                    region,
                    region_fix: regionFix
                })
            }

            /**
             * 逆地址解析（坐标位置描述）
             * @param lat
             * @param lng
             * @returns {*}
             * @link https://lbs.qq.com/service/webService/webServiceGuide/webServiceGcoder
             */
            geocoder(lat, lng) {
                return this.request.get('/ws/geocoder/v1', {
                    location: `${lat},${lng}`
                })
            }
        }

        return new AddrHelper()
    })
}(typeof define === 'function' && define.amd ? define : function (deps, factory) {
    var MOD_NAME = 'addrHelper';
    if (typeof module !== 'undefined' && module.exports) { //Node
        module.exports = factory(require('jquery'));
    } else if (window.layui && layui.define) { //layui
        layui.define(deps, function (exports) {
            exports(MOD_NAME, factory(layui.jquery, layui.layer));
        });
    } else { //window
        window[MOD_NAME] = factory(window.jQuery);
    }
}))